#include "canvas.h"

BEGIN_EVENT_TABLE(GLCanvas, wxGLCanvas)
    EVT_KEY_DOWN(GLCanvas::OnKeyDown)
    EVT_ENTER_WINDOW(GLCanvas::OnEnterWindow)
    EVT_PAINT(GLCanvas::OnPaint)
    EVT_ERASE_BACKGROUND(GLCanvas::OnEraseBackground)
    EVT_SIZE(GLCanvas::OnResize)
    EVT_MOUSE_EVENTS(GLCanvas::OnMouse)
END_EVENT_TABLE()

GLCanvas::GLCanvas(wxFrame* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : wxGLCanvas(parent, wxID_ANY, pos, size, style, name){
    firstdraw = true;
    MyContext = new wxGLContext(this,NULL);

	xRot = 0;
	yRot = 0;
	zRot = 0;
    lastPosRot = Point( 0.0, 0.0, 0.0 );
	xTrans = 0;
	yTrans = 0;
	zTrans = 10;
    lastPosTrans = Point( 0.0, 0.0, 0.0 );

	light0_pos[0] = -20.0;
	light0_pos[1] = 10.0;
	light0_pos[2] = -20.0;
	light0_pos[3] = 0.0;
    light0_color[0] = 0.8;
    light0_color[1] = 0.8;
    light0_color[2] = 0.8;
    light0_color[3] = 1.0;

    highlightOption = 0;
    wireframeOption = false;
    controlPointsOption = false;
}

GLCanvas::~GLCanvas(){
}

/*void GLCanvas::LoadFile(const wxString& _filename){
    std::cout << "Lendo arquivo" << std::endl;
    wxFileInputStream stream(_filename);
    if (stream.Ok()){
        octree = true;
        octrees.clear();

        wxTextInputStream text(stream);

        wxString tree;
        text >> tree;

        std::string treeSTD ( tree.mb_str() );

        std::cout << treeSTD << std::endl;

        Octree* octree = new Octree(octreeMax, octreeMin, 0);
        octree->makeTreeFromFile(treeSTD);
        octrees.push_back(octree);

    }
}*/

void GLCanvas::OnPaint(wxPaintEvent& event){
    wxPaintDC(this);
    if (firstdraw){
        SetCurrent(*MyContext);
        InitGL();
        ResetProjectionMode();
        firstdraw = false;
    }

	glClearColor(0.3, 0.3, 0.3, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();
	glTranslated(xTrans, 0.0, 0.0);
	glTranslated(0.0, yTrans, 0.0);
	glTranslated(0.0, 0.0, zTrans);
	glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
	glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
	glRotated(zRot / 16.0, 0.0, 0.0, 1.0);

    Render();
    SwapBuffers();
 }

void GLCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event)){
    // empty function, to avoid flashing.
}

void GLCanvas::InitGL(){
    glDisable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_DITHER);
    glShadeModel(GL_SMOOTH);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);

    glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light0_color);


    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);


    //glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
}

void GLCanvas::OnResize(wxSizeEvent& event){
    if(!firstdraw) ResetProjectionMode();
}

void GLCanvas::ResetProjectionMode(){
    int width = GetSize().x;
    int height = GetSize().y;
    if (GetContext()) {
        SetCurrent(*MyContext);

        glViewport(0, 0, width, height);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        double size = 1.0;
        if (width<=height)
            glFrustum(-size, size, -size*((double)height)/width, size*((double)height)/width,     1.0, 1000.0);
        else
            glFrustum(-size*((double)width)/height, size*((double)width)/height,     -size, size,     1.0, 1000.0);

        gluLookAt(0.0,15.0,15.0,  0.0,10.0,10.0,  0.0,1.0,0.0);
        glMatrixMode(GL_MODELVIEW);
	}
}

void GLCanvas::drawAxis(){
	glPushMatrix();
	glLoadIdentity();
	glTranslated(-230.0, 0.0, 0.0);
	glTranslated(0.0, -200.0, 0.0);
	glTranslated(0.0, 0.0, 10.0);
	glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
	glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
	glRotated(zRot / 16.0, 0.0, 0.0, 1.0);

	glColor3f(1.0, 0.0, 0.0);
	glBegin(GL_LINES);
        glNormal3f(1., 0., 0.);
		glVertex3f(0,0,0);
		glVertex3f(5,0,0);
	glEnd();

	glColor3f(0.0, 1.0, 0.0);
	glBegin(GL_LINES);
        glNormal3f(0., 1., 0.);
		glVertex3f(0,0,0);
		glVertex3f(0,5,0);
	glEnd();

	glColor3f(0.0, 0.0, 1.0);
	glBegin(GL_LINES);
        glNormal3f(0., 0., 1.);
		glVertex3f(0,0,0);
		glVertex3f(0,0,5);
	glEnd();

	glPopMatrix();
}

void GLCanvas::drawLight(){
	glPushMatrix();
	glTranslated(light0_pos[0], light0_pos[1], light0_pos[2]);

	glColor3f(light0_color[0], light0_color[1], light0_color[2]);
//meio
    double raio = 0.3;
	int passo = 20;
	double beta, beta1, alfa;
	Point p1, p2, p1n, p2n;

	for(beta = 90*M_PI/180.0; beta > -90*M_PI/180.0; beta -= passo*M_PI/180.0 ){
		beta1 = beta - 20*M_PI/180.0;
		glBegin(GL_TRIANGLE_STRIP);
			for(alfa = 0*M_PI/180.0; alfa <= 360*M_PI/180.0; alfa += passo*M_PI/180.0){

                p1.x = raio*cos(alfa)*cos(beta);
                p1.y = raio*sin(beta);
                p1.z = raio*sin(alfa)*cos(beta);
                p2.x = raio*cos(alfa)*cos(beta1);
                p2.y = raio*sin(beta1);
                p2.z = raio*sin(alfa)*cos(beta1);

                p1n = p1.normalization()*(-1);
                p2n = p2.normalization()*(-1);

                glNormal3f(p1n.x, p1n.y, p1n.z);
				glVertex3f(p1.x, p1.y, p1.z);
                glNormal3f(p2n.x, p2n.y, p2n.z);
				glVertex3f(p2.x, p2.y, p2.z);
			}
		glEnd();
	}
//polos
//	glBegin(GL_TRIANGLE_FAN);
//		beta = 90*M_PI/180.0;
//		glVertex3f(0, raio, 0);
//		for(alfa = 0*M_PI/180.0; alfa <= 360*M_PI/180.0; alfa += passo*M_PI/180.0){
//			glVertex3f(raio*cos(alfa)*cos(beta), raio*sin(beta), raio*sin(alfa)*cos(beta));
//		}
//	glEnd();
//
//	glBegin(GL_TRIANGLE_FAN);
//		beta = -90*M_PI/180.0;
//		glVertex3f(0,-raio,0);
//		for(alfa = 0*M_PI/180.0; alfa <= 360*M_PI/180.0; alfa += passo*M_PI/180.0){
//			glVertex3f(raio*cos(alfa)*cos(beta), raio*sin(beta), raio*sin(alfa)*cos(beta));
//		}
//	glEnd();

	glPopMatrix();
}

void GLCanvas::drawFloor(){
    glLineWidth(1);

    int p = 50;

    glColor3f(0.9, 0.9, 0.9);
    glNormal3f(0, 0, 0);
    glBegin(GL_LINES);
        for (int i = -(p - 1); i <= (p - 1); i++,i++){
            glVertex3f(-p, 0., i);
            glVertex3f(p, 0., i);
            glVertex3f(i, 0., -p);
            glVertex3f(i, 0., p);
        }
    glEnd();
}

void GLCanvas::Render(){
    glColor3f(0.4, 0.4, 0.4);

    drawFloor();
    drawAxis();
    //drawLight();
    scene.draw(highlightOption, wireframeOption, controlPointsOption);
}

void GLCanvas::OnMouse(wxMouseEvent& event){
    if (event.Dragging()){
        if(event.ControlDown()){
            int dx = 0, dy = 0, dz = 0;
            if (event.LeftIsDown() && event.RightIsDown()) {
                dz = event.GetX() - lastPosRot.x;
            }
            else if (event.LeftIsDown()) {
                dx = event.GetX() - lastPosRot.x;
            }
            else if (event.RightIsDown()){
                dy = event.GetY() - lastPosRot.y;
            }
            scene.movePoints(Point(-dx,-dy,-dz)*(1/100.0));
            Refresh();
        }
        else{
            int dx = event.GetX() - lastPosRot.x;
            int dy = event.GetY() - lastPosRot.y;

            if (event.RightIsDown()) {
                setXRotation(xRot + 8 * dy);
                setZRotation(zRot + 8 * dx);
            } else if (event.LeftIsDown()) {
                setXRotation(xRot + 8 * dy);
                setYRotation(yRot + 8 * dx);
            }
        }
    }
    lastPosRot = Point( event.GetX(), event.GetY(), 0.0 );
}

void GLCanvas::setXTranslation(int d){
    xTrans += d;
    Refresh();
}

void GLCanvas::setYTranslation(int d){
    yTrans += d;
    Refresh();
}

void GLCanvas::setZTranslation(int d){
    zTrans += d;
    Refresh();
}

void GLCanvas::setXRotation(int angle){
	normalizeAngle(&angle);
	if (angle != xRot) {
		xRot = angle;
		Refresh();
	}
}

void GLCanvas::setYRotation(int angle){
	normalizeAngle(&angle);
	if (angle != yRot) {
		yRot = angle;
		Refresh();
	}
}

void GLCanvas::setZRotation(int angle){
	normalizeAngle(&angle);
	if (angle != zRot) {
		zRot = angle;
		Refresh();
	}
}

void GLCanvas::normalizeAngle(int *angle){
	while (*angle < 0)
		*angle += 360 * 16;
	while (*angle > 360 * 16)
		*angle -= 360 * 16;
}

void GLCanvas::OnEnterWindow( wxMouseEvent& WXUNUSED(event) ){
    SetFocus();
}

void GLCanvas::OnKeyDown( wxKeyEvent& event ){
    int passo = 3;
    long key = event.GetKeyCode();

    if (event.ShiftDown())
        passo = 1;


    if((key == WXK_DOWN)){
        if (event.ControlDown()){
            setZTranslation(-passo);
        }
        else setYTranslation(passo);
    }
    else if(key == WXK_UP){
        if (event.ControlDown()) setZTranslation(passo);
        else setYTranslation(-passo);
    }
    else if(key == WXK_RIGHT){
        setXTranslation(-passo);
    }
    else if(key == WXK_LEFT){
        setXTranslation(passo);
    }
    else{
        event.Skip();
        return;
    }

    Refresh();
}

void GLCanvas::setHighligtOption(unsigned int _ho){
    highlightOption = _ho;
}

void GLCanvas::setWireframeOption(bool _wo){
    wireframeOption = _wo;
}

void GLCanvas::setControlPointsOption(bool _cpo){
    controlPointsOption = _cpo;
}
